// TestRAM.cpp : Defines the entry point for the DLL.
// (C) 2016 Dacris Software Inc.
//

#include "stdafx.h"
#include <windows.h>
#include <malloc.h>
#include "BmarkPlugins.h"

#define TEST_TITLE_STR	"RAM I/O"
#define TEST_UNIT_STR	"MB/s"
#define BUF_SIZE		393216
#define UPDATE_FREQ		250

extern "C" {
	void* pTestBuf1;
	void* pTestBuf2;
	BmarkApp* pBmarkApp;
	PluginData pluginData;
	PluginData progressData;
	BmarkNav navData;
	BmarkResult bmarkResult;

	HANDLE hThread;
	DWORD testThreadID;
	BOOL bThreadCancel;

	int RAMTest()
	{
		FillMemory(pTestBuf1, BUF_SIZE, 0);
		ZeroMemory(pTestBuf2, BUF_SIZE);
		CopyMemory(pTestBuf2, pTestBuf1, BUF_SIZE);
		CopyMemory(pTestBuf1, pTestBuf2, BUF_SIZE);
		return 6 * BUF_SIZE;
	}

	DWORD WINAPI TestThread(LPVOID lpThreadParameter)
	{
		PluginData *app = (PluginData*)lpThreadParameter;
		DWORD startTicks = GetTickCount();
		DWORD currentTicks = startTicks;
		DWORD lastUpdate = 0;
		int totalKB = 0;
		DWORD totalDuration = pBmarkApp->settings.iTestDuration
			* pBmarkApp->settings.iNumPasses
			* 1000;
		pBmarkApp->isTestRunning = TRUE;
		while (currentTicks - startTicks < totalDuration)
		{
			totalKB += RAMTest()/1024;
			currentTicks = GetTickCount();

			if ((currentTicks - startTicks) / UPDATE_FREQ != lastUpdate)
			{
				if (bThreadCancel)
				{
					pBmarkApp->isTestRunning = FALSE;
					ExitThread(1);
					return 1;
				}
				lastUpdate = (currentTicks - startTicks) / UPDATE_FREQ;
				progressData.iValue = (100 * (currentTicks - startTicks)) / totalDuration;
				progressData.pValue = NULL;

				pluginData.iValue = BMARK_SET_PROGRESS;
				pluginData.pValue = &progressData;
				AppMessage(app, &pluginData);
			}
		}

		pBmarkApp->isTestRunning = FALSE;
		DWORD result = totalKB / ((currentTicks - startTicks) / 1000);
		result = result / 1024;

		pluginData.iValue = BMARK_HIDE_PROGRESS;
		pluginData.pValue = NULL;
		AppMessage(app, &pluginData);

		bmarkResult.result = result;

		pluginData.iValue = BMARK_SET_RESULT;
		pluginData.pValue = &bmarkResult;
		AppMessage(app, &pluginData);

		ExitThread(0);
		return 0;
	}

	void __declspec(dllexport) DllPluginFunction(int ordinal, PluginData *app)
	{
		if (ordinal == BMARK_ATTACH)
		{
			navData.cbTitle = lstrlen(TEST_TITLE_STR) + 1;
			navData.iOrdinal = 0;
			lstrcpy(navData.lpszTitle, TEST_TITLE_STR);

			pluginData.iValue = BMARK_REG_NAV;
			pluginData.pValue = &navData;
			
			AppMessage(app, &pluginData);

			lstrcpy(bmarkResult.unit, TEST_UNIT_STR);
			lstrcpy(bmarkResult.title, TEST_TITLE_STR);

			pluginData.iValue = BMARK_REG_RESULT;
			pluginData.pValue = &bmarkResult;

			AppMessage(app, &pluginData);
		}
		if (ordinal == BMARK_SETUP)
		{
			pBmarkApp = (BmarkApp*)(app->pValue);
			pTestBuf1 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, BUF_SIZE);
			pTestBuf2 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, BUF_SIZE);
		}
		if (ordinal == BMARK_EXEC)
		{
			pluginData.iValue = BMARK_SHOW_PROGRESS;
			pluginData.pValue = NULL;
			AppMessage(app, &pluginData);

			bThreadCancel = FALSE;
			if (hThread)
			{
				CloseHandle(hThread);
			}
			hThread = CreateThread(NULL, 0, TestThread, app, 0, &testThreadID);
		}
		if (ordinal == BMARK_CANCEL_TEST)
		{
			bThreadCancel = TRUE;
		}
		if (ordinal == BMARK_EXIT)
		{
			pBmarkApp = (BmarkApp*)(app->pValue);
			HeapFree(GetProcessHeap(), 0, pTestBuf1);
			HeapFree(GetProcessHeap(), 0, pTestBuf2);
			if (hThread)
			{
				CloseHandle(hThread);
			}
		}
	}
};